package sardine;

import sardine.function.Filter;
import sardine.function.Route;
import sardine.log.Logs;
import sardine.route.RouteMatcherFactory;
import sardine.route.SimpleRouteMatcher;
import sardine.servlet.SardineFilter;
import sardine.webserver.MatcherFilter;
import sardine.webserver.SardineServer;
import sardine.webserver.SardineServerFactory;

import java.util.Objects;
import java.util.Optional;

/**
 * @auth bruce-sha
 * @date 2015/5/21
 */
public abstract class SardineBase {

    /* 公共参数 */
    public static final int DEFAULT_PORT = 9527;
    public static final String DEFAULT_HOST = "127.0.0.1";
    public static final String DEFAULT_ACCEPT_TYPE = "*/*";
    public static final String DEFAULT_CONTENT_TYPE = "text/html";
    public static final String ALL_PATHS = "!(*%)!!^";

    protected static volatile boolean started = false;

    protected static String host = DEFAULT_HOST;
    protected static int port = DEFAULT_PORT;

    // 这些要不要干掉？
    protected static String keystoreFile;
    protected static String keystorePassword;
    protected static String truststoreFile;
    protected static String truststorePassword;

    protected static Optional<String> staticFileFolder = Optional.empty();
    protected static Optional<String> externalStaticFileFolder = Optional.empty();

    protected static SardineServer server;
    protected static SimpleRouteMatcher routeMatcher;
    private static boolean runFromServlet;

    private static boolean servletStaticLocationSet;
    private static boolean servletExternalStaticLocationSet;

    public static synchronized void host(String ip) {
        assertBeforeRouteMapping();
        Sardine.host = Objects.requireNonNull(ip);
    }

    public static synchronized void port(int port) {
        assertBeforeRouteMapping();
        Sardine.port = port;
    }

    public static synchronized void notFound(String notFoundMeassage) {
//        if (started) assertBeforeRouteMapping();
        MatcherFilter.NOT_FOUND = notFoundMeassage;
    }

    /**
     * Set the connection to be secure, using the specified keystore and
     * truststore. This has to be called BEFORE any route mapping is done. You
     * have to supply a keystore file, truststore file is optional (keystore
     * will be reused).
     * This method is only relevant when using embedded Jetty servers. It should
     * not be used if you are using Servlets, where you will need to secure the
     * connection in the servlet container
     *
     * @param keystoreFile       The keystore file location as string
     * @param keystorePassword   the password for the keystore
     * @param truststoreFile     the truststore file location as string, leave null to reuse
     *                           keystore
     * @param truststorePassword the trust store password
     */
    public static synchronized void secure(String keystoreFile,
                                           String keystorePassword,
                                           String truststoreFile,
                                           String truststorePassword) {
        assertBeforeRouteMapping();

        if (keystoreFile == null) {
            throw new IllegalArgumentException(
                    "Must provide a keystore file to run secured");
        }

        Sardine.keystoreFile = keystoreFile;
        Sardine.keystorePassword = keystorePassword;
        Sardine.truststoreFile = truststoreFile;
        Sardine.truststorePassword = truststorePassword;
    }

    public static synchronized void staticFileLocation(String folder) {
//        if (started && !runFromServlet) {
        assertBeforeRouteMapping();
//        }
        staticFileFolder = Optional.ofNullable(folder);
        if (!servletStaticLocationSet) {
            if (runFromServlet) {
                SardineFilter.configureStaticResources(staticFileFolder.get());
                servletStaticLocationSet = true;
            }
        } else {
            Logs.warn("Static file location has already been set");
        }
    }


    public static synchronized void externalStaticFileLocation(String externalFolder) {
//        if (started && !runFromServlet) {
        assertBeforeRouteMapping();
//        }
        externalStaticFileFolder = Optional.ofNullable(externalFolder);
        if (!servletExternalStaticLocationSet) {
            if (runFromServlet) {
                SardineFilter.configureExternalStaticResources(externalStaticFileFolder.get());
                servletExternalStaticLocationSet = true;
            }
        } else {
            Logs.warn("External static file location has already been set");
        }
    }

    private static void assertBeforeRouteMapping() {
        if (started) throw new IllegalStateException("This must be done BEFORE route mapping has begun");
    }

    private static boolean hasStaticFileHandlers() {
        return staticFileFolder.isPresent() || externalStaticFileFolder.isPresent();
    }


    /**
     * Stops the Spark server and clears all routes
     */
    public static synchronized void stop() {
        if (server != null) {
            routeMatcher.clearRoutes();
            server.stop();
        }
        started = false;
    }

    static synchronized void runFromServlet() {
        runFromServlet = true;
        if (!started) {
            routeMatcher = RouteMatcherFactory.singleton();
            started = true;
        }
    }

    protected static SimpleRoute build(final String path, final Route route) {
        return build(path, Optional.of(DEFAULT_ACCEPT_TYPE), route);
    }

    protected static SimpleRoute build(final String path, final Optional<String> acceptType, final Route route) {
        return new SimpleRoute(path, acceptType.orElse(DEFAULT_ACCEPT_TYPE)) {
            @Override
            public Object apply(Request request, Response response) throws Exception {
                return route.apply(request, response);
            }
        };
    }

    /**
     * Wraps the filter in FilterImpl
     *
     * @param path   the path
     * @param filter the filter
     * @return the wrapped route
     */
    protected static SimpleFilter build(final String path, final Filter filter) {
        return build(path, Optional.of(DEFAULT_ACCEPT_TYPE), filter);
    }

    protected static SimpleFilter build(final String path, final Optional<String> acceptType, final Filter filter) {
        return new SimpleFilter(path, acceptType.orElse(DEFAULT_ACCEPT_TYPE)) {
            @Override
            public void apply(Request request, Response response) throws Exception {
                filter.apply(request, response);
            }
        };
    }

    protected static <T> void route(final HttpMethod httpMethod, final SimpleRoute<T> route) {
        start();
        routeMatcher.parseValidateAddRoute(httpMethod, route.getPath(), route.getAcceptType(), route);
    }

    protected static void filter(final HttpMethod httpMethod, final SimpleFilter filter) {
        start();
        routeMatcher.parseValidateAddRoute(httpMethod, filter.getPath(), filter.getAcceptType(), filter);
    }

    protected static synchronized void start() {
        if (started) return;

        routeMatcher = RouteMatcherFactory.singleton();

//        Stream.of(new Thread()).forEach(Thread::start);

        Thread thread = new Thread(() -> {
            server = SardineServerFactory.create(hasStaticFileHandlers());
            //点火
            server.fire(
                    host,
                    port,
                    keystoreFile,
                    keystorePassword,
                    truststoreFile,
                    truststorePassword,
                    staticFileFolder,
                    externalStaticFileFolder);
        });
        thread.start();
        //TODO:Noddd.consumer
//        try {
////            thread.join();
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        started = true;
    }

    //TODO:设置调试模式
    protected static synchronized void debug(boolean debug) {

    }
}
